package hn.sigit.logic.ladmshadow;

import hn.sigit.logic.ladm.ParcelUtil;
import hn.sigit.logic.ladm.ResponsibilitiesUtil;
import hn.sigit.logic.ladm.RestrictionsUtil;
import hn.sigit.logic.ladm.RightsUtil;
import hn.sigit.model.hnd.cadastre.HND_Parcel;
import hn.sigit.model.hnd.cadastre.HND_Property;
import hn.sigit.model.hnd.cadastre.dao.HND_ParcelDAO;
import hn.sigit.model.hnd.cadastre.dao.HND_PropertyDAO;
import hn.sigit.model.hnd.ladmshadow.LAPoint;
import hn.sigit.model.hnd.ladmshadow.Parcel;
import hn.sigit.model.hnd.ladmshadow.Party;
import hn.sigit.model.hnd.ladmshadow.Property;
import hn.sigit.model.hnd.ladmshadow.RRR;
import hn.sigit.model.hnd.ladmshadow.Responsibility;
import hn.sigit.model.hnd.ladmshadow.Restriction;
import hn.sigit.model.hnd.ladmshadow.Right;
import hn.sigit.model.hnd.ladmshadow.SpatialUnit;
import hn.sigit.model.hnd.ladmshadow.dao.ParcelDAO;
import hn.sigit.model.hnd.ladmshadow.dao.PartyDAO;
import hn.sigit.model.hnd.ladmshadow.dao.SpatialUnitDAO;
import hn.sigit.model.ladm.administrative.LA_RRR;
import hn.sigit.model.ladm.administrative.LA_Responsibility;
import hn.sigit.model.ladm.administrative.LA_Restriction;
import hn.sigit.model.ladm.administrative.LA_Right;
import hn.sigit.model.ladm.administrative.dao.LA_RRRDAO;
import hn.sigit.model.ladm.party.LA_Party;
import hn.sigit.model.ladm.spatialunit.LA_SpatialUnit;
import hn.sigit.model.ladm.spatialunit.surveyingandrepresentation.LA_Point;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.vividsolutions.jts.geom.Polygon;

public class ShadowToLADM {
	private static Set<Long> parcelSetToLadmIdSet(Set<Parcel> parcelSet) {
		Set<Long> ladmIdSet = new HashSet<Long>();
		for (Parcel p : parcelSet)
			ladmIdSet.add(p.getLadmId());
		
		return ladmIdSet;
	}
	
	public static void applyPresentationChanges(long presentationId) {
		Map<Set<Long>, Set<Parcel>> replacementMap = new HashMap<Set<Long>, Set<Parcel>>();
		//retrieves all parcels including requested and neighbors ones
		List<Parcel> finalParcels = ParcelDAO.loadFinalParcelsByPresentationId(presentationId);
		
		
		//for each parcel p, retrieve their original predecessors
		for (Parcel p : finalParcels) {
			Set<Parcel> predecessorSet = originalParcelPredecessors(p);
			Set<Long> ladmIdSet = parcelSetToLadmIdSet(predecessorSet);
			
			if (replacementMap.containsKey(ladmIdSet)) {
				replacementMap.get(ladmIdSet).add(p);
			}
			else {
				Set<Parcel> newParcelSet = new HashSet<Parcel>();
				newParcelSet.add(p);
				replacementMap.put(ladmIdSet, newParcelSet);
			}
		}
		
		Date timeStamp = new Date();
		long currentTime = timeStamp.getTime();
		int parcelTimeOffset = 0;
		
		HND_Parcel hndParcel;
		Set<Parcel> replacementSet;
		Set<Set<Long>> setOfLadmIdSets = replacementMap.keySet();
		for (Set<Long> ladmIdKeySet : setOfLadmIdSets) {
			replacementSet = replacementMap.get(ladmIdKeySet);
			timeStamp.setTime(currentTime + parcelTimeOffset);
			
			//the parcel stored at the model has to be killed
			//unless it does not have a predecessor different than itself
			if (ladmIdKeySet.size() == 1 && replacementSet.size() == 1) {
				//This case is when no merge/split operations were done
				//We have to deal with 3 cases:
				//0: No changes at all
				//1: The geometry didn't change, just the simple attributes
				//2: The geometry changed as a result of topological corrections
				//In any case, always check for RRR changes
				
				Long singleLadmIdKey = ladmIdKeySet.iterator().next();
				Parcel replParcel = replacementSet.iterator().next();
				hndParcel = HND_ParcelDAO.loadParcelById(singleLadmIdKey);
				
				Property replProperty = replParcel.getProperty();
				HND_Property hndProperty = hndParcel.getProperty();
				
				if (!ParcelUtil.parcelsEqual(hndParcel, replParcel)) {
					//CASE 1 or 2
					if (ParcelUtil.geomEquals(hndParcel.getShape(), replParcel.getShape())) {
						//CASE 1:
						//just replace the simple attributes without changing anything else
						//TODO: Manage the log of attribute changes
						ParcelUtil.copySimpleAttributes(hndParcel, replParcel);
						HND_ParcelDAO.save(hndParcel);
					}
					else {
						//CASE 2:
						//kill the parcel and create a new one
						hndParcel.setEndLifespanVersion(timeStamp);
						hndProperty.setEndLifespanVersion(timeStamp);
						
						HND_Property newHndProperty = PropertyToHND_Property(replProperty);
						HND_Parcel newHndParcel = ParcelToHND_Parcel(replParcel);
						
						newHndProperty.getSpatialUnits().clear();
						newHndProperty.getSpatialUnits().add(newHndParcel);
						
						newHndParcel.getBaunits().clear();
						newHndParcel.getBaunits().add(newHndProperty);
						
						HND_PropertyDAO.save(newHndProperty);
						HND_ParcelDAO.save(newHndParcel);
					}
				}
				
				if (!RightsUtil.propertyRightsEqual(hndProperty, replProperty)) {
					//if the replacement property has an RRR that the original parcel
					//does not, we have to add it
					for (RRR rrr : replProperty.getRrr()) {
						if (rrr instanceof Right) {
							Right right = (Right) rrr;
							if (!RightsUtil.propertyHasRight(hndProperty, right)) {
								LA_Right newLaRight = (LA_Right) RRRToLA_RRR(right);
								newLaRight.setrID(0);
								newLaRight.setBeginLifespanVersion(timeStamp);
								
								if (hndProperty.getRrr() == null)
									hndProperty.setRrr(new HashSet<LA_RRR>());
								hndProperty.getRrr().add(newLaRight);
								
								LA_RRRDAO.save(newLaRight);
							}
						}
						else if (rrr instanceof Restriction) {
							Restriction restriction = (Restriction) rrr;
							if (!RestrictionsUtil.propertyHasRestriction(hndProperty, restriction)) {
								LA_Restriction newLaRestriction = (LA_Restriction) RRRToLA_RRR(restriction);
								newLaRestriction.setrID(0);
								newLaRestriction.setBeginLifespanVersion(timeStamp);
								
								if (hndProperty.getRrr() == null)
									hndProperty.setRrr(new HashSet<LA_RRR>());
								hndProperty.getRrr().add(newLaRestriction);
								
								LA_RRRDAO.save(newLaRestriction);
							}
						}
						else if (rrr instanceof Responsibility) {
							Responsibility responsibility = (Responsibility) rrr;
							if (!ResponsibilitiesUtil.propertyHasResponsibility(hndProperty, responsibility)) {
								LA_Responsibility newLaResponsibility = (LA_Responsibility) RRRToLA_RRR(responsibility);
								newLaResponsibility.setrID(0);
								newLaResponsibility.setBeginLifespanVersion(timeStamp);
								
								if (hndProperty.getRrr() == null)
									hndProperty.setRrr(new HashSet<LA_RRR>());
								hndProperty.getRrr().add(newLaResponsibility);
								
								LA_RRRDAO.save(newLaResponsibility);
							}
						}
						
					}
					HND_PropertyDAO.save(hndProperty);
					HND_ParcelDAO.save(hndParcel);

					//if the original property has an RRR that the replacement parcel
					//does not, we kill it
					for (LA_RRR laRrr : hndProperty.getRrr()) {
						if (laRrr instanceof LA_Right) {
							LA_Right laRight = (LA_Right) laRrr;
							if (!RightsUtil.propertyHasRight(replProperty, laRight))
								laRrr.setEndLifespanVersion(timeStamp);
						}
						else if (laRrr instanceof LA_Restriction) {
							LA_Restriction laRestriction = (LA_Restriction) laRrr;
							if (!RestrictionsUtil.propertyHasRestriction(replProperty, laRestriction))
								laRestriction.setEndLifespanVersion(timeStamp);
						}
						else if (laRrr instanceof LA_Responsibility) {
							LA_Responsibility laResponsibility = (LA_Responsibility) laRrr;
							if (!ResponsibilitiesUtil.propertyHasResponsibility(replProperty, laResponsibility))
								laResponsibility.setEndLifespanVersion(timeStamp);
						}
						
						LA_RRRDAO.save(laRrr);
					}
				}
				
			}
			else {
				//In this case the parcel is always to be killed
				for (Long ladmIdKey : ladmIdKeySet) {
					//Kill every parcel and its propery in the predecessors set
					hndParcel = HND_ParcelDAO.loadParcelById(ladmIdKey);
					hndParcel.setEndLifespanVersion(timeStamp);
					hndParcel.getProperty().setEndLifespanVersion(timeStamp);
					
					
					HND_PropertyDAO.save(hndParcel.getProperty());
					HND_ParcelDAO.save(hndParcel);
				}
				
				
				for (Parcel parcel : replacementSet) {
					//Create all new parcels and properties according to the replacement set
					//The RRR beginning life timestamp will be set as the creation of the parcel
					// instead of the original parcel
					HND_Property newHndProperty = createNewHndPropertyFromParcel(parcel, timeStamp, true);
					HND_PropertyDAO.save(newHndProperty);
				}
			}

			parcelTimeOffset += 10;
		}
	}
	
	private static HND_Property createNewHndPropertyFromParcel(Parcel parcel, Date timeStamp, boolean applyTimeStampToRRR) {
		Property property = parcel.getProperty();
		
		HND_Parcel newHndParcel = ParcelToHND_Parcel(parcel);
		if ((Polygon) parcel.getShape() != null)
			newHndParcel.setShape( (Polygon) parcel.getShape().clone() );
		newHndParcel.setBeginLifespanVersion(timeStamp);
		
		HND_Property newHndProperty = PropertyToHND_Property(property);
		newHndProperty.setBeginLifespanVersion(timeStamp);
		newHndProperty.setRrr(SetRRR_To_SetLARRR(property.getRrr(), applyTimeStampToRRR, timeStamp));
		
		Set<LA_SpatialUnit> suSet = new HashSet<LA_SpatialUnit>();
		suSet.add(newHndParcel);
		newHndProperty.setSpatialUnits(suSet);
		
		return newHndProperty;
	}
	
	private static Set<Parcel> originalParcelPredecessors(Parcel parcel) {
		Set<Parcel> retval = new HashSet<Parcel>();
		List<Parcel> workingList = new ArrayList<Parcel>();

		workingList = ParcelDAO.loadParcelsByEndLifespanVersion(parcel.getBeginLifespanVersion());
		if (workingList != null && workingList.size() > 0) {
			for (Parcel p : workingList)
				retval.addAll(originalParcelPredecessors(p));
		}
		else
			retval.add(parcel);
		
		return retval;
	}
	
	private static void replaceParcel(HND_Parcel hndOriginalParcel, Parcel newParcel) {
	}
	
	private boolean areRightsEqual() {
		return true;
	}
	
	
	public static HND_Parcel ParcelToHND_Parcel(Parcel parcel) {
		if (parcel == null) return null;
		
		HND_Parcel hndParcel = new HND_Parcel();
		Util.copyMemberValues(parcel, hndParcel, true);
		hndParcel.setSuID(0);
		
		return hndParcel;
	}
	
	public static HND_Property PropertyToHND_Property(Property property) {
		if (property == null) return null;
		
		HND_Property hndProperty = new HND_Property();
		Util.copyMemberValues(property, hndProperty, true);
		hndProperty.setuID(0);
		
		return hndProperty;
	}
	
	public static LA_RRR RRRToLA_RRR(RRR rrr) {
		if (rrr == null) return null;
		
		LA_RRR laRrr = null;
		
		if (rrr instanceof Responsibility)
			laRrr = new LA_Responsibility();
		else if (rrr instanceof Right)
			laRrr = new LA_Right();
		else if (rrr instanceof Restriction)
			laRrr = new LA_Restriction();

		if (rrr != null) {
			Util.copyMemberValues(laRrr, rrr, true);
			laRrr.setrID(0);
			laRrr.setShare(rrr.getShare());
		}
		
		return laRrr;
	}

	public static Set<LA_RRR> SetRRR_To_SetLARRR(Set<RRR> rrrSet, boolean applyTimeStamp, Date beginLifespanTimeStamp) {
		if (rrrSet == null) return null;
		
		LA_RRR laRrr;
		Set<LA_RRR> laRrrSet = new HashSet<LA_RRR>();
		for (RRR rrr : rrrSet) {
			if (rrr.getEndLifespanVersion() != null) {
				laRrr = RRRToLA_RRR(rrr);
				if (applyTimeStamp)
					rrr.setBeginLifespanVersion(beginLifespanTimeStamp);
					
				laRrrSet.add(laRrr);
			}
		}
		
		return laRrrSet;
	}
}
